/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.explorer.ui.reports;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import org.activiti.engine.ActivitiException;
import org.activiti.explorer.ExplorerApp;
import org.activiti.explorer.Messages;
import org.activiti.explorer.ui.mainlayout.ExplorerLayout;
import org.dussan.vaadin.dcharts.DCharts;
import org.dussan.vaadin.dcharts.base.elements.XYaxis;
import org.dussan.vaadin.dcharts.base.elements.XYseries;
import org.dussan.vaadin.dcharts.base.renderers.MarkerRenderer;
import org.dussan.vaadin.dcharts.data.DataSeries;
import org.dussan.vaadin.dcharts.data.Ticks;
import org.dussan.vaadin.dcharts.metadata.LegendPlacements;
import org.dussan.vaadin.dcharts.metadata.XYaxes;
import org.dussan.vaadin.dcharts.metadata.renderers.AxisRenderers;
import org.dussan.vaadin.dcharts.metadata.renderers.LabelRenderers;
import org.dussan.vaadin.dcharts.metadata.renderers.SeriesRenderers;
import org.dussan.vaadin.dcharts.metadata.styles.MarkerStyles;
import org.dussan.vaadin.dcharts.options.*;

import java.util.Iterator;

/**
 * @author Joram Barrez
 */
public class ChartGenerator {

    public static final String CHART_TYPE_BAR_CHART = "barChart";
    public static final String CHART_TYPE_PIE_CHART = "pieChart";
    public static final String CHART_TYPE_LINE_CHART = "lineChart";
    public static final String CHART_TYPE_LIST = "list";

    public static ChartComponent generateChart(byte[] reportData) {

        // Convert json to pojo
        JsonNode jsonNode = convert(reportData);

        // Title
        JsonNode titleNode = jsonNode.get("title");
        String title = null;
        if (titleNode != null) {
            title = titleNode.textValue();
        }

        ChartComponent chartComponent = new ChartComponent(title);


        // Retrieve data sets
        JsonNode datasetsNode = jsonNode.get("datasets");

        // If no data was returned
        if (datasetsNode.size() == 0) {
            chartComponent.addChart(null, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
            return chartComponent;
        }

        if (datasetsNode != null && datasetsNode.isArray()) {

            Iterator<JsonNode> dataIterator = datasetsNode.iterator();
            while (dataIterator.hasNext()) {

                JsonNode datasetNode = dataIterator.next();

                JsonNode descriptionNode = datasetNode.get("description");
                String description = null;
                if (descriptionNode != null) {
                    description = descriptionNode.textValue();
                }
                JsonNode dataNode = datasetNode.get("data");

                if (dataNode == null || dataNode.size() == 0) {
                    chartComponent.addChart(description, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
                } else {

                    String[] names = new String[dataNode.size()];
                    Number[] values = new Number[dataNode.size()];

                    int index = 0;
                    Iterator<String> fieldIterator = dataNode.fieldNames();
                    while (fieldIterator.hasNext()) {
                        String field = fieldIterator.next();
                        names[index] = field;
                        values[index] = dataNode.get(field).numberValue();
                        index++;
                    }

                    // Generate chart (or 'no data' message)
                    if (names.length > 0) {
                        Component chart = createChart(datasetNode, names, values);
                        chartComponent.addChart(description, chart, null);
                    } else {
                        chartComponent.addChart(description, null, ExplorerApp.get().getI18nManager().getMessage(Messages.REPORTING_ERROR_NOT_ENOUGH_DATA));
                    }

                }

            }

        }


        return chartComponent;
    }

    protected static Component createChart(JsonNode dataNode, String[] names, Number[] values) {
        String type = dataNode.get("type").textValue();

        JsonNode xAxisNode = dataNode.get("xaxis");
        String xAxis = null;
        if (xAxisNode != null) {
            xAxis = xAxisNode.textValue();
        }

        JsonNode yAxisNode = dataNode.get("yaxis");
        String yAxis = null;
        if (yAxisNode != null) {
            yAxis = yAxisNode.textValue();
        }

        Component chart = null;
        if (CHART_TYPE_BAR_CHART.equals(type)) {

            DataSeries dataSeries = new DataSeries().add((Object[]) values);
            SeriesDefaults seriesDefaults = new SeriesDefaults().setRenderer(SeriesRenderers.BAR);
            Axes axes = new Axes().addAxis(new XYaxis().setRenderer(AxisRenderers.CATEGORY).setTicks(new Ticks().add((Object[]) names)));
            Highlighter highlighter = new Highlighter().setShow(false);

            Options options = new Options().setSeriesDefaults(seriesDefaults).setAxes(axes).setHighlighter(highlighter);
            options.setAnimate(true);
            options.setAnimateReplot(true);

            chart = new DCharts().setDataSeries(dataSeries).setOptions(options);

        } else if (CHART_TYPE_PIE_CHART.equals(type)) {

            DataSeries dataSeries = new DataSeries().newSeries();
            for (int i = 0; i < names.length; i++) {
                dataSeries.add(names[i], values[i]);
            }
            SeriesDefaults seriesDefaults = new SeriesDefaults().setRenderer(SeriesRenderers.PIE);

            Options options = new Options().setSeriesDefaults(seriesDefaults);
            options.setAnimate(true);
            options.setAnimateReplot(true);

            Legend legend = new Legend().setShow(true).setPlacement(LegendPlacements.INSIDE);
            options.setLegend(legend);

            Highlighter highlighter = new Highlighter().setShow(true);
            options.setHighlighter(highlighter);

            chart = new DCharts().setDataSeries(dataSeries).setOptions(options);

        } else if (CHART_TYPE_LINE_CHART.equals(type)) {

            AxesDefaults axesDefaults = new AxesDefaults().setLabelRenderer(LabelRenderers.CANVAS);
            Axes axes = new Axes()
                    .addAxis(new XYaxis().setLabel(xAxis != null ? xAxis : "").setMin(names[0]).setMax(names[values.length - 1]).setDrawMajorTickMarks(true))
                    .addAxis(new XYaxis(XYaxes.Y).setLabel(yAxis != null ? yAxis : "").setDrawMajorTickMarks(true));
            Options options = new Options().setAxesDefaults(axesDefaults).setAxes(axes);
            DataSeries dataSeries = new DataSeries().newSeries();
            for (int i = 0; i < names.length; i++) {

//        if (parseLong(names[i]) != null) {
//          dataSeries.add(parseLong(names[i]), values[i]);
//        } else if (parseDouble(names[i]) != null) {
//          dataSeries.add(parseDouble(names[i]), values[i]);
//        } else {
//          dataSeries.add(names[i], values[i]);
//        }

                dataSeries.add(names[i], values[i]);

            }

            Series series = new Series().addSeries(
                    new XYseries().setShowLine(true).setMarkerOptions(new MarkerRenderer().setShadow(true).setSize(7).setStyle(MarkerStyles.CIRCLE)));
            options.setSeries(series);

            options.setAnimate(true);
            options.setAnimateReplot(true);

            Highlighter highlighter = new Highlighter().setShow(true);
            options.setHighlighter(highlighter);

            chart = new DCharts().setDataSeries(dataSeries).setOptions(options);

        } else if (CHART_TYPE_LIST.equals(type)) {

            GridLayout grid = new GridLayout(2, names.length);
            grid.setSpacing(true);

            for (int i = 0; i < names.length; i++) {
                String name = names[i];
                Label nameLabel = new Label(name);
                nameLabel.addStyleName(ExplorerLayout.STYLE_LABEL_BOLD);
                grid.addComponent(nameLabel, 0, i);

                Number value = values[i];
                Label valueLabel = new Label(value + "");
                grid.addComponent(valueLabel, 1, i);
            }

            chart = grid;

        }

        if (chart instanceof DCharts) {
            // Needed, otherwise the chart will not be shown
            ((DCharts) chart).show();
        }

        return chart;
    }

    protected static JsonNode convert(byte[] jsonBytes) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readTree(jsonBytes);
        } catch (Exception e) {
            throw new ActivitiException("Report dataset contains invalid json", e);
        }
    }

    protected static Long parseLong(String s) {
        try {
            Long value = Long.parseLong(s);
            return value;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    protected static Double parseDouble(String s) {
        try {
            Double value = Double.parseDouble(s);
            return value;
        } catch (NumberFormatException e) {
            return null;
        }
    }

}
